/*
 ============================================================================
 Name        : DerivedTypeEx.c
 Author      : mattia
 Version     :
 Copyright   : Your copyright notice
 Description : Compute Pi in MPI C++
 ============================================================================
 */

#include <stdio.h>
//#include <mpi.h>
#include <mpi/mpi.h>
#include <pthread.h>
#include "DppmParams.h"
#include "Utils.h"
#include "LoadBalancing.h"
#include "BestSolutionExchange.h"
#include "TuplesProcessing.h"
#include "Tuple.h"


#define MASTER 0
#define PRINTMASTER 1

int rank, size;
DppmParams * dppm;
SortedBuffer * tuples;
pthread_mutex_t tuples_mutex = PTHREAD_MUTEX_INITIALIZER;
Tuple * bestSolution;
pthread_mutex_t best_solution_mutex = PTHREAD_MUTEX_INITIALIZER;
bool end = false;
pthread_mutex_t	end_flag_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t idle_cond = PTHREAD_COND_INITIALIZER;


int main(int argc, char** argv){


	int numAct,	T;
	int* numPreds;
	int* numModes;
	int** Preds;
	int** ModesCosts;
	int** ModesTimes;


	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);


	if (!rank){

		if ((argc != 2) || readDppmSpec(argv[1],numAct,T,numPreds,Preds,numModes,ModesCosts,ModesTimes)){
				printf("Error!!\n");
				return -1;
		}
//		cout<<"Leido DppmParams info"<<endl;
//		cout<<"Enviando DppmParams info"<<endl;
	}else{
		//cout<<"Recibiendo DppmParams info"<<endl;
	}

	MPI_Bcast(&numAct,1,MPI_INT, MASTER, MPI_COMM_WORLD);
	MPI_Bcast(&T,1,MPI_INT, MASTER, MPI_COMM_WORLD);
	if(rank){
		numPreds = new int[numAct];
		numModes = new int[numAct];
		Preds = new int*[numAct];
		ModesCosts = new int*[numAct];
		ModesTimes = new int*[numAct];
	}
	MPI_Bcast(numPreds, numAct, MPI_INT, MASTER, MPI_COMM_WORLD);
	MPI_Bcast(numModes, numAct, MPI_INT, MASTER, MPI_COMM_WORLD);

	for(int i = 0; i < numAct; i++){
		if(rank){
			Preds[i] = new int[numPreds[i]];
			ModesCosts[i] = new int[numModes[i]];
			ModesTimes[i] = new int[numModes[i]];
		}
		MPI_Bcast(Preds[i], numPreds[i], MPI_INT, MASTER, MPI_COMM_WORLD);
		MPI_Bcast(ModesCosts[i], numModes[i], MPI_INT, MASTER, MPI_COMM_WORLD);
		MPI_Bcast(ModesTimes[i], numModes[i], MPI_INT, MASTER, MPI_COMM_WORLD);
	}

	Tuple::SetTuplesIntSize(numAct);

	bestSolution = new Tuple(numAct);

	tuples = new SortedBuffer();

	dppm = new DppmParams(numAct,T,numPreds,Preds,numModes,ModesCosts,ModesTimes);

	cout<<"Creado DppmParams, rank = "<<rank<<endl;

	pthread_t loadBalancingThread = createThread(&LoadBalancing);
	pthread_t bestSolutionExchangeThread = createThread(&BestSolutionExchange);
    pthread_t tuplesProcessingThread = createThread(&TuplesProcessing);

//    cout<<"Prontos los hilos de ejecucion, rank = "<<rank<<endl;

    pthread_join(loadBalancingThread, NULL);
    pthread_join(bestSolutionExchangeThread, NULL);
    pthread_join(tuplesProcessingThread, NULL);

    cout<<"Esperando join, rank = "<<rank<<endl;

    int recvBuff[size][Tuple::tuplesIntSize];

    //Compartir soluciones con un Gather al root
    //MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
    //void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
    //MPI_Comm comm)
    MPI_Gather(bestSolution->getArray(),sizeof(int) * Tuple::tuplesIntSize,MPI_INT,
    				recvBuff,sizeof(int) * Tuple::tuplesIntSize * size,MPI_INT,0,MPI_COMM_WORLD);
    if (!rank){

    	//Decido la mejor solucion
    	int BSIndex = 0;
    	int BSCost = bestSolution->cost();

		for(int i = 1; i < size; i++){
			if (Tuple::GetCostFromTupleArray(recvBuff[i]) < BSCost){
				BSCost = Tuple::GetCostFromTupleArray(recvBuff[i]);
				BSIndex = i;
			}
		}

		if(BSIndex != rank){
			delete bestSolution;
			bestSolution = new Tuple(Tuple::tuplesIntSize,recvBuff[Tuple::tuplesIntSize * BSIndex]);
		}

		printf("La mejor solución es:");
		bestSolution->print();

    }

	MPI_Finalize();

	delete dppm;
	delete tuples;
	delete bestSolution;

	return 0;
}



